/*!
 * \file DLLHelper.hpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief 动态库辅助工具类，主要是把跨平台的差异封装起来，简化使用
 * 
 * \details 提供跨平台的动态库操作功能，包括：
 *          - 动态库加载和卸载
 *          - 函数符号获取和调用
 *          - 跨平台文件名处理
 *          - 异常安全的库操作
 *          
 *          支持Windows (.dll) 和 Linux (.so) 两个平台，
 *          统一了不同平台下动态库操作的API差异，
 *          为插件系统和模块化架构提供基础支持。
 */
#pragma once
#include <string>

#ifdef _MSC_VER
#include <wtypes.h>
typedef HMODULE		DllHandle;    ///< Windows动态库句柄类型
typedef void*		ProcHandle;   ///< Windows函数指针类型
#else
#include <dlfcn.h>
typedef void*		DllHandle;    ///< Linux动态库句柄类型
typedef void*		ProcHandle;   ///< Linux函数指针类型
#endif

/*!
 * \brief 动态库辅助工具类
 * 
 * \details 提供跨平台的动态库操作静态方法集合，包括：
 *          - 动态库的加载、卸载和符号获取
 *          - 跨平台的文件名处理和路径封装
 *          - 异常安全的错误处理机制
 *          - 统一的API接口设计
 *          
 *          该类专为WonderTrader框架的模块化架构设计，
 *          支持策略插件、行情插件、交易插件等的动态加载，
 *          是实现插件式扩展的核心基础工具。
 */
class DLLHelper
{
public:
	/*!
	 * \brief 加载动态库
	 * \param filename 动态库文件名或完整路径
	 * \return 动态库句柄，失败时返回NULL
	 * 
	 * \details 跨平台加载动态库文件：
	 *          - Windows: 使用LoadLibrary API
	 *          - Linux: 使用dlopen，RTLD_NOW模式立即解析所有符号
	 *          
	 *          支持相对路径和绝对路径，失败时会在Linux平台
	 *          输出详细的错误信息。具有异常安全保证。
	 */
	static DllHandle load_library(const char *filename)
	{
		try
		{
			// 检查输入参数有效性
			if (filename == nullptr || strlen(filename) == 0)
			{
				return NULL;
			}

#ifdef _MSC_VER
			return ::LoadLibrary(filename);
#else
			DllHandle ret = dlopen(filename, RTLD_NOW);
			if (ret == NULL)
				printf("%s\n", dlerror());
			return ret;
#endif
		}
		catch(...)
		{
			return NULL;
		}
	}

	/*!
	 * \brief 释放动态库
	 * \param handle 要释放的动态库句柄
	 * 
	 * \details 安全释放动态库资源：
	 *          - 自动检查句柄有效性，避免重复释放
	 *          - Windows: 使用FreeLibrary API
	 *          - Linux: 使用dlclose API
	 *          
	 *          调用后所有从该库获取的函数指针将失效，
	 *          需要确保不再使用相关的函数指针。
	 */
	static void free_library(DllHandle handle)
	{
		if (NULL == handle)
			return;

#ifdef _MSC_VER
		::FreeLibrary(handle);
#else
		dlclose(handle);
#endif
	}

	/*!
	 * \brief 获取动态库中的函数符号
	 * \param handle 动态库句柄
	 * \param name 函数名称
	 * \return 函数指针，失败时返回NULL
	 * 
	 * \details 从已加载的动态库中获取指定函数的地址：
	 *          - Windows: 使用GetProcAddress API
	 *          - Linux: 使用dlsym API
	 *          
	 *          获取的函数指针可以强制转换为实际的函数类型进行调用。
	 *          注意函数名称需要与动态库中导出的符号名称完全匹配。
	 */
	static ProcHandle get_symbol(DllHandle handle, const char* name)
	{
		if (NULL == handle)
			return NULL;

#ifdef _MSC_VER
		return ::GetProcAddress(handle, name);
#else
		return dlsym(handle, name);
#endif
	}

	/*!
	 * \brief 包装模块名为平台特定的文件名
	 * \param name 模块基础名称（不含扩展名和前缀）
	 * \param unixPrefix Unix系统下的文件名前缀，默认为"lib"
	 * \return 完整的平台特定文件名
	 * 
	 * \details 根据不同平台生成对应的动态库文件名：
	 *          - Windows: name.dll
	 *          - Linux: unixPrefix + name + .so (默认为lib + name + .so)
	 *          
	 *          例如：wrap_module("strategy") 在Windows下返回"strategy.dll"，
	 *               在Linux下返回"libstrategy.so"
	 */
	static std::string wrap_module(const char* name, const char* unixPrefix = "lib")
	{

#ifdef _WIN32
		std::string ret = name;
		ret += ".dll";
		return std::move(ret);
#else
		std::string ret(unixPrefix);
		ret += name;
		ret += ".so";
		return std::move(ret);
#endif
	}
};